package xg.glycomaid.utils;


import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import org.xml.sax.*;
import org.xml.sax.helpers.DefaultHandler;

public class SAXUtils
{
    public static interface SAXWriter
    {

        public abstract void write(TransformerHandler transformerhandler)
            throws SAXException;
    }

    public static class DocumentHandler extends ObjectTreeHandler
    {

        public ObjectTreeHandler getHandler(String namespaceURI, String localName, String qName)
        {
            if(root_handler != null && root_handler.isElement(namespaceURI, localName, qName))
                return root_handler;
            else
                return null;
        }

        private ObjectTreeHandler root_handler;

        public DocumentHandler(ObjectTreeHandler _root)
        {
            root_handler = null;
            root_handler = _root;
        }
    }

    public static class ObjectTreeHandler extends DefaultHandler
    {

        public String getText()
        {
            return text.toString();
        }

        public Object getObject()
        {
            return object;
        }

        protected boolean isElement(String namespaceURI, String localName, String qName)
        {
            return false;
        }

        protected ObjectTreeHandler getHandler(String namespaceURI, String localName, String qName)
            throws SAXException
        {
            return null;
        }

        protected void initContent(String namespaceURI, String localName, String qName, Attributes atts)
            throws SAXException
        {
            object = null;
            text = new StringBuilder();
            sub_objects = new HashMap();
        }

        protected void addText(char buf[], int start, int length)
            throws SAXException
        {
            for(int i = 0; i < length; i++)
                text.append(buf[start + i]);

        }

        protected void addWhiteSpace()
            throws SAXException
        {
            text.append(' ');
        }

        protected void addObject(String name, Object o)
        {
            if(o != null)
            {
                Vector v = (Vector)sub_objects.get(name);
                if(v == null)
                    sub_objects.put(name, v = new Vector());
                v.add(o);
            }
        }

        protected Object getSubObject(String name, boolean assert_non_null)
            throws SAXException
        {
            Vector v = (Vector)sub_objects.get(name);
            if(v == null)
                if(assert_non_null)
                    throw new SAXException(createMessage((new StringBuilder()).append("Cannot find object with name ").append(name).toString()));
                else
                    return null;
            if(v.size() > 1)
                throw new SAXException(createMessage((new StringBuilder()).append("Multiple objects with name ").append(name).toString()));
            else
                return v.get(0);
        }

        protected Object getSubObject(String name, Object default_value)
            throws SAXException
        {
            Vector v = (Vector)sub_objects.get(name);
            if(v == null)
                return default_value;
            if(v.size() > 1)
                throw new SAXException(createMessage((new StringBuilder()).append("Multiple objects with name ").append(name).toString()));
            else
                return v.get(0);
        }

        protected Vector getSubObjects(String name)
        {
            Vector v = (Vector)sub_objects.get(name);
            if(v == null)
                return new Vector();
            else
                return v;
        }

        protected Object finalizeContent(String namespaceURI, String localName, String qName)
            throws SAXException
        {
            return object;
        }

        protected String stringAttribute(Attributes atts, String name, String default_value)
        {
            if(atts == null || name == null)
                return default_value;
            if(atts.getValue(name) == null)
                return default_value;
            else
                return atts.getValue(name);
        }

        protected String stringAttribute(Attributes atts, String name)
        {
            return stringAttribute(atts, name, null);
        }

        protected Double doubleAttribute(Attributes atts, String name, Double default_value)
        {
            if(atts == null || name == null)
                return default_value;
            if(atts.getValue(name) == null)
                return default_value;
            else
                return Double.valueOf(atts.getValue(name));
        }

        protected Double doubleAttribute(Attributes atts, String name)
        {
            return doubleAttribute(atts, name, null);
        }

        protected Integer integerAttribute(Attributes atts, String name, Integer default_value)
        {
            if(atts == null || name == null)
                return default_value;
            if(atts.getValue(name) == null)
                return default_value;
            else
                return Integer.valueOf(atts.getValue(name));
        }

        protected Integer integerAttribute(Attributes atts, String name)
        {
            return integerAttribute(atts, name, null);
        }

        protected Boolean booleanAttribute(Attributes atts, String name, Boolean default_value)
        {
            if(atts == null || name == null)
                return default_value;
            if(atts.getValue(name) == null)
                return default_value;
            else
                return Boolean.valueOf(atts.getValue(name));
        }

        protected Boolean booleanAttribute(Attributes atts, String name)
        {
            return booleanAttribute(atts, name, null);
        }

        protected String createMessage(String message)
        {
            if(theLocator == null)
                return message;
            else
                return (new StringBuilder()).append(message).append(" at line ").append(theLocator.getLineNumber()).append(" column ").append(theLocator.getColumnNumber()).toString();
        }

        protected String createMessage(Exception e)
        {
            if(e == null)
                return createMessage("Exception");
            else
                return createMessage(e.getMessage());
        }

        public void startDocument()
            throws SAXException
        {
            if(handlers_stack.size() != 0)
            {
                throw new SAXException(createMessage("Unexpected start of document"));
            } else
            {
                pushHandler(new DocumentHandler(this));
                return;
            }
        }

        public void startElement(String namespaceURI, String localName, String qName, Attributes atts)
            throws SAXException
        {
            if(handlers_stack.size() == 0)
                new SAXException(createMessage("Unexpected start of element"));
            ObjectTreeHandler handler = currentHandler().getHandler(namespaceURI, localName, qName);
            if(handler == null)
                handler = new ObjectTreeHandler();
            handler.initContent(namespaceURI, localName, qName, atts);
            pushHandler(handler);
        }

        public void characters(char ch[], int start, int length)
            throws SAXException
        {
            if(handlers_stack.size() == 0)
                new SAXException(createMessage("Unexpected character content"));
            currentHandler().addText(ch, start, length);
        }

        public void ignorableWhitespace(char ch[], int start, int length)
            throws SAXException
        {
            if(handlers_stack.size() == 0)
                new SAXException(createMessage("Unexpected character content"));
            currentHandler().addWhiteSpace();
        }

        public void endElement(String namespaceURI, String localName, String qName)
            throws SAXException
        {
            if(handlers_stack.size() == 0)
                new SAXException(createMessage("Unexpected end of element"));
            Object sub_object = currentHandler().finalizeContent(namespaceURI, localName, qName);
            popHandler();
            if(sub_object != null)
                currentHandler().addObject(qName, sub_object);
        }

        public void endDocument()
            throws SAXException
        {
            if(handlers_stack.size() == 0)
                new SAXException(createMessage("Unexpected end of document"));
            popHandler();
        }

        public void setDocumentLocator(Locator locator)
        {
            theLocator = locator;
        }

        private void pushHandler(ObjectTreeHandler handler)
            throws SAXException
        {
            if(handler == null)
            {
                throw new SAXException(createMessage("Invalid null handler"));
            } else
            {
                handlers_stack.addFirst(handler);
                return;
            }
        }

        private void popHandler()
            throws SAXException
        {
            if(handlers_stack.size() == 0)
            {
                throw new SAXException(createMessage("Empty stack"));
            } else
            {
                handlers_stack.removeFirst();
                return;
            }
        }

        private ObjectTreeHandler currentHandler()
            throws SAXException
        {
            if(handlers_stack.size() == 0)
                throw new SAXException(createMessage("Empty stack"));
            else
                return (ObjectTreeHandler)handlers_stack.getFirst();
        }

        protected Locator theLocator;
        protected LinkedList handlers_stack;
        protected Object object;
        protected StringBuilder text;
        protected HashMap sub_objects;

        public ObjectTreeHandler()
        {
            theLocator = null;
            handlers_stack = new LinkedList();
            object = null;
            text = new StringBuilder();
            sub_objects = new HashMap();
        }
    }


    public SAXUtils()
    {
    }

    public static void read(InputStream is, DefaultHandler h)
        throws Exception
    {
        SAXParser parser = SAXParserFactory.newInstance().newSAXParser();
        parser.parse(is, h);
    }

    public static void write(OutputStream os, SAXWriter w)
        throws Exception
    {
        StreamResult streamResult = new StreamResult(os);
        SAXTransformerFactory tf = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
        TransformerHandler hd = tf.newTransformerHandler();
        Transformer serializer = hd.getTransformer();
        serializer.setOutputProperty("encoding", "ISO-8859-1");
        serializer.setOutputProperty("indent", "yes");
        hd.setResult(streamResult);
        hd.startDocument();
        w.write(hd);
        hd.endDocument();
    }
}

